using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using Common;

namespace WebDiver
{
    public partial class MainWindow : Form
    {
        Data data;
        public static ApplicationData applicationData;
        public static MainWindow mainWindow;
        
        // concurrent child windows
        XSLTWindow frmXSLT;
 
        public MainWindow()
        {
            InitializeComponent();

            data = new Data();
            applicationData = new ApplicationData();
            engineList = new List<Data.Engine>();
            mainWindow = this;
            
            // init concurrent child windows (but do not show)
            frmXSLT = new XSLTWindow();
            Reset();
        }

        private bool isChanged = false;
        static public string fileName = "";

        static public void Notice(string msg)
        {
            MessageBox.Show(msg);
        }
        
        public void Reset()
        {
            theTreeView.Nodes.Clear();
            theComboBox.Items.Clear();
            currentNode = null;
            BrowseNew();
        }

        // if false, then cancel was pressed.
        private bool saveChanges()
        {
            if (isChanged)
            {
                DialogResult result = MessageBox.Show(
                    "File changed. Do you want to save your changes?",
                    "Web Diver",
                    MessageBoxButtons.YesNoCancel);
		
                if (result == DialogResult.Yes)
                {
                    onSaveDocument();
                    if (isChanged) return false;
                }
                else if (result == DialogResult.No)
                {
                    return true;
                }
                else if (result == DialogResult.Cancel)
                {
                    return false;
                }
            }
            return true;
        }
        
        private void enableReload(bool value)
        {
            mnuReload.Enabled = value;
        }

        private void setTitleBar()
        {
            if (fileName == "")
                Text = "Web Diver";
            else
                Text = "Web Diver - " + fileName;
        }

        private void FillGUIData()
        { 
            // clear our current control contents
            Reset();

            // fill tree
            foreach (Data.TermTree tTree in data.termTrees)
            {
                FillTreeView(theTreeView, tTree.tree);
            }
        }

        //
        // *** START CODE FROM NodeTree example 
        //
        // This code is only used in the above FillGUIData() function
        //

        private class TreeViewUpdate : IDisposable
        {
            private TreeView TreeView = null;

            public TreeViewUpdate(TreeView TreeView)
            {
                this.TreeView = TreeView;
                TreeView.BeginUpdate();
            }

            public void Dispose()
            {
                TreeView.EndUpdate();
            }        
        }

        private void FillTreeView(TreeView TreeView, ITree tree)
        {
            using (new TreeViewUpdate(TreeView))
            {
                if (tree == null) return;
                foreach (INode child in tree.Root.DirectChildren)
                {
                    AddNodes(TreeView.Nodes, tree.Root);
                }
            }
        }

        private void AddNodes(TreeNodeCollection nodes, INode parent)
        {
            foreach (INode child in parent.DirectChildren)
            {
                Data.Term term = (Data.Term)child.Data;
                TreeNode node = nodes.Add(term.name);
                node.Tag = child;
                AddNodes(node.Nodes, child);
            }
            return;
        }

        //
        // *** RESUME ORIGINAL CODE
        //

        private void onNewDocument()
        {
            if (!saveChanges()) return;

            data.Reset();
            Reset();

            NewFileWizard frm = new NewFileWizard();
            frm.ShowDialog();

            fileName = "";
            enableReload(false);
            isChanged = false;
            // Update title bar to include new name
            setTitleBar();
        }
        
        private void onOpenDocument()
        {
            if (!saveChanges()) return;

            {
                OpenFileDialog dlg = new OpenFileDialog();
                dlg.Title = "Open WebDiver XML";
                dlg.Filter = "Webdiver files (*.xml)|*.xml|All files (*.*)|*.*";
                if (dlg.ShowDialog() == DialogResult.OK)
                {
                    DoOpenFile(dlg.FileName);
                }
                dlg.Dispose();
            }
        }

        public void DoOpenFile(string filename)
        {
            fileName = filename;

            if (data.ReadFromFile(fileName) == true)
            {
                FillGUIData();
                setTitleBar();
                enableReload(true);
                AddRecentFile();
            }
            else
            {
                MessageBox.Show("File Open FAILED. Starting a blank document. [FIXME]");
                onNewDocument();
            }
        }
        
        private void onReloadDocument()
        {
            if (!saveChanges()) return;
            DoOpenFile(fileName);
        }
        
        private void onSaveDocument()
        {
            if (fileName == "")
                onSaveAsDocument();
            else
            {
                data.WriteToFile(fileName);

                isChanged = false;
                // Update title bar to include new name
                setTitleBar();
            }
        }
        
        private void onSaveAsDocument()
        {
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.Title = "Save WebDiver XML";
            dlg.DefaultExt = "xml";
            dlg.Filter = "Webdiver files (*.xml)|*.xml|"
            + "All files|*.*";
            dlg.InitialDirectory = "";
            dlg.RestoreDirectory = true;

            if (dlg.ShowDialog() == DialogResult.OK)
            {
                fileName = dlg.FileName;
                onSaveDocument();
                AddRecentFile();
            }

            dlg.Dispose();

        }
        
        private void onViewBack()
        {
            webBrowser.GoBack();
        }
        
        private void onViewForward()
        {
            webBrowser.GoForward();
        }
        
        private void onViewStop()
        {
            webBrowser.Stop();
        }
        
        private void onViewRefresh()
        {
            webBrowser.Refresh();
        }
        
        private void onGo()
        {
            webBrowser.Navigate(tstbAddress.ToString());
        }

        //
        // Most .NET generated event handlers are trivial wrappers
        //
        
        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        { System.Windows.Forms.Application.Exit(); }

        private void toolStrip_LayoutCompleted(object sender, EventArgs e)
        { tstbAddress.Width = (tsbGo.Bounds.Left - tstbAddress.Bounds.Left) - 1; }

        private void toolStrip_Layout(object sender, LayoutEventArgs e)
        { tstbAddress.Width = 1; }

        private void tsbNew_Click(object sender, EventArgs e)
        { onNewDocument(); }

        private void toolStripMenuItem2_Click(object sender, EventArgs e)
        { onNewDocument(); }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        { onOpenDocument(); }

        private void tsbOpen_Click(object sender, EventArgs e)
        { onOpenDocument(); }

        private void toolStripMenuItem1_Click(object sender, EventArgs e)
        { onReloadDocument(); }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        { onSaveDocument(); }

        private void tsbSave_Click(object sender, EventArgs e)
        { onSaveDocument(); }

        private void saveasToolStripMenuItem_Click(object sender, EventArgs e)
        { onSaveAsDocument(); }

        private void backToolStripMenuItem_Click(object sender, EventArgs e)
        { onViewBack(); }

        private void forwardToolStripMenuItem_Click(object sender, EventArgs e)
        { onViewForward(); }

        private void stopToolStripMenuItem_Click(object sender, EventArgs e)
        { onViewStop(); }

        private void refreshToolStripMenuItem_Click(object sender, EventArgs e)
        { onViewRefresh(); }

        private void tsbNavBack_Click(object sender, EventArgs e)
        { onViewBack(); }

        private void tsbNavForward_Click(object sender, EventArgs e)
        { onViewForward(); }

        private void tsbNavStop_Click(object sender, EventArgs e)
        { onViewStop(); }

        private void tsbNavRefresh_Click(object sender, EventArgs e)
        { onViewRefresh(); }

        private void tsbGo_Click(object sender, EventArgs e)
        { onGo(); }

        private void tstbAddress_KeyPress(object sender, KeyPressEventArgs e)
        { if (e.KeyChar == '\r') onGo(); }

        private void webBrowser_Navigated(object sender, WebBrowserNavigatedEventArgs e)
        { tstbAddress.Text = webBrowser.Url.ToString(); }

//        private void touchCurrentFileToolStripMenuItem_Click(object sender, EventArgs e)
//        {  }

        private void verifyInternalDataToolStripMenuItem_Click(object sender, EventArgs e) 
        { data.TreeVerify(); }

        //
        // MainWindow_Load() - read file when it's given on cmdline
        //

        private void MainWindow_Load(object sender, EventArgs e)
        {
            // load application data from registry
            applicationData.PersistentLoad();
            
            // Add XML editors to main menu
            MainMenuUpdateEditors();
            // Add Recent Files
            MainMenuUpdateRecentFiles();

            // get filename and load it, if given on the commandline
            if (System.Environment.GetCommandLineArgs().Length == 2)
            {
                // get data filename from commandline
                fileName = System.Environment.GetCommandLineArgs()[1];
                if (fileName[0] == '\"' && fileName[fileName.Length - 1] == '\"')
                {
                    fileName = fileName.Replace("\"", "");
                }
                onReloadDocument();
                setTitleBar();
                enableReload(true);
                AddRecentFile();
            }
        }


        /// <summary>
        /// EditorMenuItem and related
        /// </summary>


        public class EditorMenuItem : ToolStripMenuItem
        {
            string mDescription;
            string mExeName;

            public EditorMenuItem(string description, string exename)
                : base(description)
            {
                mDescription = description;
                mExeName = exename;
            }
            
            public static void menuItem_Click(object sender, System.EventArgs e)
            {
                EditorMenuItem menuItem = sender as EditorMenuItem;

                System.Diagnostics.Process.Start(menuItem.mExeName,"\""+MainWindow.fileName+"\"");
            }
        }

        private void MainMenuUpdateEditors()
        {
            // 1) delete previous list, if any (keeping the 2 top items)
            int tmp = editWithToolStripMenuItem.DropDownItems.Count;
            for (int i = 2; i < tmp; i++)
                    editWithToolStripMenuItem.DropDownItems.RemoveAt(2);

            // 2) add the new enginelist
            foreach (ApplicationData.EditorItem item in applicationData.editorList)
            {
                EditorMenuItem menuItem = new EditorMenuItem(item.menuName,item.exeFilename);
                menuItem.Click += new EventHandler(EditorMenuItem.menuItem_Click);
                editWithToolStripMenuItem.DropDownItems.Add(menuItem);
            }
        }

        public class RecentFileMenuItem : ToolStripMenuItem
        {
            string mFilename;
 
            public RecentFileMenuItem(string filename)
                : base(filename)
            {
                mFilename = filename;
            }

            public static void menuItem_Click(object sender, System.EventArgs e)
            {
                RecentFileMenuItem menuItem = sender as RecentFileMenuItem;

                mainWindow.DoOpenFile(menuItem.mFilename);
            }
        }


        private void MainMenuUpdateRecentFiles()
        {
            // 1) delete previous list
            recentFilesToolStripMenuItem.DropDownItems.Clear();

            // 2) add the new enginelist
            foreach (string item in applicationData.recentFiles)
            {
                RecentFileMenuItem menuItem = new RecentFileMenuItem(item);
                menuItem.Click += new EventHandler(RecentFileMenuItem.menuItem_Click);
                recentFilesToolStripMenuItem.DropDownItems.Add(menuItem);
            }
        }

        private void AddRecentFile()
        {
            const int constMaxRecentFiles = 10;

            if (fileName != "")
            {
                List<string> tmp = new List<string>();

                tmp.Add(fileName);  // add 'filename' to top of current list
                
                foreach(string item in applicationData.recentFiles)
                {
                    if (item != fileName && item != "")
                        tmp.Add(item);

                    if (tmp.Count >= constMaxRecentFiles)
                        break;
                }

                applicationData.recentFiles.Clear();
                foreach (string s in tmp) applicationData.recentFiles.Add(s);

                MainMenuUpdateRecentFiles();
            }
        }


        private void MainWindow_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (saveChanges() == false) /* user pressed 'cancel' */
            {
                e.Cancel = true;
            }
        }

        //
        // Tree navigation event handlers.
        //
        
        TreeNode currentNode = null;
        bool passedBeforeExpand = false;

        private void theTreeView_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (currentNode == e.Node)
            {
                if (passedBeforeExpand == false)
                    BrowseNew();
                else
                    passedBeforeExpand = false;
            }
        }

        private void theTreeView_BeforeSelect(object sender, TreeViewCancelEventArgs e)
        {
            currentNode = e.Node;
            BrowseNew();
        }

        private void theTreeView_BeforeExpand(object sender, TreeViewCancelEventArgs e)
        {
            passedBeforeExpand = true;
        }

        // the combobox event handler
        private void theComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            BrowseNew();
        }

        //
        // Common event handler for when a new page must be displayed
        //

        private bool inSelectQuery = false;

        private void BrowseNew()
        {
            if (inSelectQuery) return;

            if (currentNode == null) {
                tstbAddress.Text = "about:blank";
                onGo();
                return;
            }

            // get text
            string topic = currentNode.Text;

            // 'select' query
            string query = SelectQuery();
        
            // replace $topic and $encoded_topic strings with our topic
            UTF8Encoding encoding = new UTF8Encoding();
            string encoded_topic = System.Web.HttpUtility.UrlEncode(topic, encoding);
            string result = query.Replace("$encoded_topic", encoded_topic);
            result = result.Replace("$topic", topic);
            // NEW SYNTAX
            result = query.Replace("$encoded_term", encoded_topic);
            result = result.Replace("$term", topic);

            // we want to go to this URL
            string location = result;

            // kick the gui
            tstbAddress.Text = location;
            onGo();
        }

        //
        // Cascading search engine selection
        //

        List<Data.Engine> engineList;

        private string SelectQuery()
        {
            inSelectQuery = true;
            INode currNode = (INode)currentNode.Tag;

            //
            // add engines and sets while walking to the root term
            //

            engineList.Clear();

            do
            {
                Data.Term term = (Data.Term) currNode.Data;
                
                // add them engines
                foreach (Data.Engine engine in term.engines)
                {
                    AddEngine(engine.name,engine.query);
                }

                // add them enginesets, in reverse
                List<string> tmp = term.enginesets;
                tmp.Reverse();
                foreach (string name in tmp)
                {
                    AddEngineSet(name);
                }

                // walk to the parent, if possible
                if (currNode.IsRoot) break;
                currNode = currNode.Parent;
                if (currNode.IsRoot) break;
            } while (true);

            // add root engines
            AddRootEngines();

            //
            // update listbox ctl
            //


            // first, 'remember' current engine
            string tmpEnginename = theComboBox.Text;

            theComboBox.Items.Clear();
            foreach (Data.Engine e in engineList)
            {
                theComboBox.Items.Add(e.name);
            }

            // 'restore' index
            int i=0;
            foreach (string s in theComboBox.Items)
            {
                if (s == tmpEnginename)
                {
                    theComboBox.SelectedIndex = i;
                }
                i++;
            }


            // ok, assume we got the right engine name selected
            if (theComboBox.Text == "")
            {
                inSelectQuery = false;
                return "about:blank";
            }
            // find the engine
            foreach (Data.Engine e in engineList)
            {
                if (e.name == theComboBox.Text)
                {
                    inSelectQuery = false;
                    return e.query;
                }
            }


            inSelectQuery = false;
            return "about:notreached error";
        }

        private void AddEngine(string name, string query)
        {

            // add this engine (if it not already exists)
            foreach (Data.Engine e in engineList)
            {
                if (e.name == name) return;
            }
            engineList.Add(new Data.Engine(name, query));
        }

        private void AddRootEngines()
        {
            foreach (Data.Engine e in data.rootEngines)
                AddEngine(e.name, e.query);
        }

        private void AddEngineSet(string name)
        {
            // add the contents of this engine set using AddEngine() for each engine
            foreach (Data.EngineSet engineSet in data.engineSets)
            {
                if (engineSet.name == name)
                    foreach (Data.Engine engine in engineSet)
                    {
                        AddEngine(engine.name, engine.query);
                    }
            }
        }

//        private void showDocumentStatisticsToolStripMenuItem_Click(object sender, EventArgs e)
//        {
//        }

//        private void configureEditorsToolStripMenuItem_Click(object sender, EventArgs e)
//        {
//        }

        private void runThisFileInExplorerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start("\"" + fileName + "\"");
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
        }

        private void aboutToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            About frm = new About();
            frm.Show();
        }

        private void documentatToolStripMenuItem_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start("http://code.google.com/p/iondrive/wiki/WebDiver");
        }

        private void MainWindow_FormClosed(object sender, FormClosedEventArgs e)
        {
            applicationData.PersistentSave();
        }


        private void theTreeView_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
        {
            MessageBox.Show("theTreeView_AfterLabelEdit");
        }

        private void showStatisticsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            data.TreeVerify();
        }

        private void configureEditorsToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            ConfigureEditorsForm frm = new ConfigureEditorsForm();
            frm.ShowDialog();
            MainMenuUpdateEditors();
        }

        private void touchDataToolStripMenuItem_Click(object sender, EventArgs e)
        {
            isChanged = true;
        }

        private void xSLProcessingToolStripMenuItem_Click(object sender, EventArgs e)
        {

            frmXSLT.Show(); // run and exit this function (keep window async)
        }

        private void MainWindow_DragDrop(object sender, DragEventArgs e)
        {
            try
            {
                Array a = (Array)e.Data.GetData(DataFormats.FileDrop);

                if (a != null)
                {
                    // Extract string from first array element
                    // (ignore all files except first if number of files are dropped).
                    string s = a.GetValue(0).ToString();

                    // Call OpenFile asynchronously.
                    // Explorer instance from which file is dropped is not responding
                    // all the time when DragDrop handler is active, so we need to return
                    // immidiately (especially if OpenFile shows MessageBox).

//                    this.BeginInvoke(m_DelegateOpenFile, new Object[] { s });
//
//                    this.Activate();        // in the case Explorer overlaps this form
                }
            }
            catch (Exception ex)
            {
                // MessageBox.Show("Error in DragDrop function: " + ex.Message);
                // don't show MessageBox here - Explorer is waiting !
            }
        }

        private void theTreeView_DragDrop(object sender, DragEventArgs e)
        {
            try
            {
                Array a = (Array)e.Data.GetData(DataFormats.Text);

                if (a != null)
                {
                    // Extract string from first array element
                    // (ignore all files except first if number of files are dropped).
                    string s = a.GetValue(0).ToString();
                }
            }
            catch (Exception ex)
            {
                // don't show MessageBox here - Explorer is waiting !
            }
        }

        private void theTreeView_DragEnter(object sender, DragEventArgs e)
        {

            // a verbatim string
            string s = (string) e.Data.GetData(DataFormats.Text);
            if (s != null)
            {
                // this is a string dragged into the tree
                e.Effect = DragDropEffects.Copy;
                return;
            }

            if (e.Data.GetData(DataFormats.FileDrop) != null)
            {
                // a file has been dropped into the tree.
                MessageBox.Show("A file has been dropped into the tree, import it.");
                e.Effect = DragDropEffects.Link;
                return;
            }
        }

        private void MainWindow_DragEnter(object sender, DragEventArgs e)
        {
            e.Effect = DragDropEffects.Link;
        }

    }
}